
#include "RtBase.h"
#include "RtReactorSelect.h"
#include "RtTimerQueueBase.h"

CRtReactorSelect::CRtReactorSelect()
	: CRtReactorBase(SEND_REGISTER_PROPERTY)
{
}

CRtReactorSelect::~CRtReactorSelect()
{
}

RtResult CRtReactorSelect::Open()
{
	RtResult rv = CRtReactorBase::Open();
	if (RT_FAILED(rv))
		goto fail;

	rv = m_Notify.Open(this);
	if (RT_FAILED(rv))
		goto fail;

	CRtStopFlag::SetStartFlag();
	RT_INFO_TRACE_THIS("CRtReactorSelect::Open()");
	return RT_OK;

fail:
	Close();
	RT_ASSERTE(RT_FAILED(rv));
	RT_ERROR_TRACE_THIS("CRtReactorSelect::Open, failed!"
		" rv=" << rv);
	return rv;
}

RtResult CRtReactorSelect::
NotifyHandler(IRtEventHandler *aEh, IRtEventHandler::MASK aMask)
{
	return m_Notify.Notify(aEh, aMask);
}

RtResult CRtReactorSelect::RunEventLoop()
{
	RT_INFO_TRACE_THIS("CRtReactorSelect::RunEventLoop");
	m_Est.EnsureSingleThread();

	while (!CRtStopFlag::IsFlagStopped()) {
		CRtTimeValue tvTimeout(CRtTimeValue::s_tvMax);
		if (m_pTimerQueue) {
			// process timer prior to wait event.
			m_pTimerQueue->CheckExpire(&tvTimeout);
		}

		timeval tvSelect;
		tvSelect.tv_sec = tvTimeout.GetSec();
		tvSelect.tv_usec = tvTimeout.GetUsec();

		fd_set fsRead, fsWrite, fsException;
		FD_ZERO(&fsRead);
		FD_ZERO(&fsWrite);
		FD_ZERO(&fsException);
		int nMaxFd = m_EhRepository.FillFdSets(fsRead, fsWrite, fsException);
		RT_ASSERTE(nMaxFd >= 0);

	#ifdef RT_MACOS
	#ifndef MachOSupport
	 	int nSelect = ::CFM_select(nMaxFd+1, &fsRead, &fsWrite, &fsException, 
	  	tvTimeout == CRtTimeValue::s_tvMax ? NULL : &tvSelect);
	  	if (nSelect == 0 || (nSelect == -1 && CFM_geterrno() == EINTR))
	#else
		int nSelect = ::select(nMaxFd+1, &fsRead, &fsWrite, &fsException, 
	  	tvTimeout == CRtTimeValue::s_tvMax ? NULL : &tvSelect);
	  	if (nSelect == 0 || (nSelect == -1 && errno == EINTR))
	#endif	//MachOSupport  	
	#else
		int nSelect = ::select(nMaxFd+1, &fsRead, &fsWrite, &fsException, 
			(tvTimeout == CRtTimeValue::s_tvMax ? NULL : &tvSelect));
		if (nSelect == 0 || (nSelect == -1 && errno == EINTR))
	#endif	
			continue;
		else if (nSelect == -1) {
			RT_ERROR_TRACE_THIS("CRtReactorSelect::RunEventLoop, select() failed!"
				" nMaxFd=" << nMaxFd << " err=" << errno);
			return RT_ERROR_FAILURE;
		}

		int nActiveNumber = nSelect;
		ProcessFdSets_i(
			fsRead, 
			IRtEventHandler::READ_MASK | IRtEventHandler::ACCEPT_MASK | IRtEventHandler::CONNECT_MASK, 
			nActiveNumber, nMaxFd);
		ProcessFdSets_i(
			fsWrite, 
			IRtEventHandler::WRITE_MASK | IRtEventHandler::CONNECT_MASK, 
			nActiveNumber, nMaxFd);
#ifdef RT_WIN32
		ProcessFdSets_i(
			fsException, 
			IRtEventHandler::CLOSE_MASK,
			nActiveNumber, nMaxFd);
#endif // RT_WIN32

		// Needn't check nActiveNumber due to because 
		// fd maybe removed when doing callback.
//		RT_ASSERTE(nActiveNumber == 0);
	}
	return RT_OK;
}

void CRtReactorSelect::
ProcessFdSets_i(fd_set &aFdSet, IRtEventHandler::MASK aMask, 
				int &aActiveNumber, int aMaxFd)
{
#ifdef RT_WIN32
	for (u_int i = 0; i < aFdSet.fd_count && aActiveNumber > 0; i++) {
		RT_HANDLE fdGet = (RT_HANDLE)aFdSet.fd_array[i];
		if (fdGet == RT_INVALID_HANDLE)
			continue;
		aActiveNumber--;
		ProcessHandleEvent(fdGet, aMask, RT_OK, FALSE);
	}
#else
	CRtEventHandlerRepository::CElement* pElements = m_EhRepository.GetElement();
	for (int i = 0; i <= aMaxFd; i++) {
		CRtEventHandlerRepository::CElement &eleGet = pElements[i];
		if (!eleGet.IsCleared()) {
//			RT_HANDLE fdGet = eleGet.m_pEh->GetHandle();
			if (FD_ISSET(i, &aFdSet)) {
				aActiveNumber--;
				ProcessHandleEvent(i, aMask, RT_OK, FALSE);
			}
		}
	}
#endif // RT_WIN32
}

RtResult CRtReactorSelect::StopEventLoop()
{
//	CRtStopFlag::SetStopFlag();
	CRtStopFlag::m_bStoppedFlag = TRUE;
	m_Notify.Notify(&m_Notify, IRtEventHandler::NULL_MASK);
	return RT_OK;
}

RtResult CRtReactorSelect::Close()
{
	m_Notify.Close();
	return CRtReactorBase::Close();
}

RtResult CRtReactorSelect::
OnHandleRegister(RT_HANDLE aFd, IRtEventHandler::MASK aMask, IRtEventHandler *aEh)
{
	return RT_OK;
}

void CRtReactorSelect::OnHandleRemoved(RT_HANDLE aFd)
{
}
